﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Optimization.Utilities
{
    /// <summary>
    /// This class replaces System.Random class with same functionality.
    /// Reason to use this class is ability to define one seed for all random generators in one project.
    /// All instances uses the same random generator.
    /// </summary>
    public class Random : System.Random
    {
        /// <summary>
        /// Value defining the initial state of random generator. It is used for experiment reconstruction.
        /// </summary>
        static public int Seed = -1;

        /// <summary>
        /// Static variable, any method are directed to this class. All used generators in algorithm are redirected. 
        /// This allow to reconstruction of experiments even with multiple random generators.
        /// </summary>
        static System.Random rnd = null;

        /// <summary>
        /// Initialization of Random class - static variables
        /// </summary>
        static public void Init()
        {
            //Console.WriteLine("ASeed : " + Seed.ToString());
            if (Seed == -1)
            {
                System.Random rndSeed = new System.Random();
                Seed = rndSeed.Next(16777216);
            }

            rnd = new System.Random(Seed);
            //Console.WriteLine("BSeed : " + Seed.ToString());
        }

        /// <summary>
        /// Initializes the class with seed
        /// </summary>
        /// <param name="seed">Seed for random sequence, same number mean same random sequence</param>
        static public void Init(int seed)
        {
            //Console.WriteLine("CSeed : " + Seed.ToString());
            Seed = seed;
            Init();
            //Console.WriteLine("DSeed : " + Seed.ToString());
        }

        /// <summary>
        /// Constructor for class, at first call the static fields are initialized. Do not use till you setup the seed.
        /// </summary>
        public Random()
        {
            if (rnd == null)
                Init();
        }

        /// <summary>
        /// Constructor for compatibility with same functionality as constructor Random(). The parameter is ignored.
        /// </summary>
        /// <param name="Seed"></param>
        private Random(int Seed)
            : this()
        {
        }

        /// <summary>
        /// Generate random int number.
        /// </summary>
        /// <returns></returns>
        public override int Next()
        {
            return rnd.Next();
        }

        /// <summary>
        /// Generate random number with max value.
        /// </summary>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public override int Next(int maxValue)
        {
            return rnd.Next(maxValue);
        }

        /// <summary>
        /// Generates random values from defined interval.
        /// </summary>
        /// <param name="minValue"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public override int Next(int minValue, int maxValue)
        {
            return rnd.Next(minValue, maxValue);
        }

        /// <summary>
        /// Generates double random value.
        /// </summary>
        /// <returns></returns>
        public override double NextDouble()
        {
            return rnd.NextDouble();
        }

        /// <summary>
        /// Generates random byte[] array 
        /// </summary>
        /// <param name="buffer"></param>
        public override void NextBytes(byte[] buffer)
        {
            rnd.NextBytes(buffer);
        }

        /// <summary>
        /// Same functionality as NextDouble() function - generates double random value
        /// </summary>
        /// <returns></returns>
        protected override double Sample()
        {
            return rnd.NextDouble();
        }
    }
}
